package servlet;

import config.Action;
import config.ActionMapping;
import config.Forward;
import org.apache.commons.digester.Digester;
import org.apache.commons.digester.xmlrules.DigesterLoader;
import org.xml.sax.SAXException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * Author:shixiaojun@itany.com
 * Date:2022/4/21 17:03
 * Version:1.0-SNAPSHOT
 * Description:
 */
@WebServlet(urlPatterns = "*.do",loadOnStartup = 1,initParams = {
        @WebInitParam(name = "configLocation",value = "actions.xml")
})
public class ActionServlet extends HttpServlet {

    private ActionMapping actionMapping;
    private Map<String,Object> actions;

    @Override
    public void init() throws ServletException {
        actions = new HashMap<>();
        Digester digester = DigesterLoader.createDigester(ActionServlet.class.getClassLoader().getResource("config/rule.xml"));
        actionMapping = new ActionMapping();
        digester.push(actionMapping);
        String configLocation = getInitParameter("configLocation");
        try {
            digester.parse(ActionServlet.class.getClassLoader().getResourceAsStream(configLocation));
        } catch (IOException e) {
            e.printStackTrace();
            throw new ServletException(e.getMessage());
        } catch (SAXException e) {
            e.printStackTrace();
            throw new ServletException(e.getMessage());
        }
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println(actionMapping);
        // 获取当前发送的请求
        String path = request.getServletPath();
        // 此时请求是以.do结尾的,但是配置文件中并没有配置后缀
        // 因此,可以将请求的后缀去除后再去配置文件中查找对应的配置
        path = path.substring(0,path.lastIndexOf("."));
        System.out.println("path:"+path);
        // 此时获取的path即为action标签的path属性
        // 可以根据path获取到action标签所映射的Java对象Action
        Action action = actionMapping.findAction(path);
        if(null == action){
            System.out.println("请求"+path+"在配置文件中没有找到对应的配置");
            return;
        }
        try {
            Class actionClass = Class.forName(action.getType());
            // 根据type属性获取集合中的对象
            Object obj = actions.get(action.getType());
            // 如果没有找到对应的对象,说明尚未创建过该对象
            if(null == obj){
                // 根据type生成与之对应的对象
                obj = actionClass.newInstance();
                // 创建成果之后,将该对象存放到集合中,便于下次使用
                actions.put(action.getType(),obj);
            }

            // 获取对应的方法
            // 参数一:执行的目标方法名
            // 参数二:可变长参数,指定目标方法的参数列表中参数的Class类型
            // 通过参数二可控制目标方法的具体类型
            // 如果目标方法的参数类型与当前指定的类型不一致,无法运行
            // 且目标方法的参数的顺序也必须与此处顺序一致
            Method method = actionClass.getMethod(action.getParameter(),HttpServletRequest.class,HttpServletResponse.class);

            // 获取目标方法返回值
            String result = (String) method.invoke(obj,request,response);
            // 如果返回值为null,则结束
            if(null == result){
                System.out.println("当前方法无返回值");
                return;
            }
            // 根据返回值找到对应的Forward配置
            Forward forward = action.findForward(result);
            // 如果返回值在配置文件中没有找到配置,则结束
            if(null == forward){
                System.out.println("返回值"+result+"在配置文件中没有对应的配置");
                return;
            }

            // 根据redirect属性判断当前页面跳转使用转发还是重定向
            // 页面跳转的地址为path属性
            // 如果redirect属性值为true则表示使用重定向
            if(forward.isRedirect()){
                response.sendRedirect(request.getContextPath()+forward.getPath());
                return;
            }
            // 否则使用转发
            request.getRequestDispatcher(forward.getPath()).forward(request,response);

        } catch (Exception e) {
            e.printStackTrace();
            throw new ServletException(e.getMessage());
        }
    }

}
